//
// Created by Chen qin lang on 2018/7/13.
//

#include "WlVideo.h"

WlVideo::WlVideo(WlPlaystatus *playstatus, WlCallJava *callJava) : callJava(callJava),
                                                                   playstatus(playstatus) {
    queue = new WlQueue(playstatus);
    pthread_mutex_init(&codecMutex, NULL);
}

void *decode(void *context) {
    WlVideo *video = static_cast<WlVideo *>(context);
    video->decodeThread();
    return 0;
    //pthread_exit(&video->play_thread);
}

void WlVideo::decodeThread() {

    while (playstatus != NULL && !playstatus->exit) {
        if (playstatus->seek) {
            av_usleep(1000 * 100);
            continue;
        }
        if (playstatus->pause) {
            av_usleep(1000 * 100);
            continue;
        }
        if (queue->getQueueSize() == 0) {
            if (!playstatus->load) {
                playstatus->load = true;
                callJava->onCallLoad(CHILD_THREAD, true);
            }
            av_usleep(1000 * 100);
            continue;
        } else {
            if (playstatus->load) {
                playstatus->load = false;
                callJava->onCallLoad(CHILD_THREAD, false);
            }
        }
        AVPacket *avPacket = av_packet_alloc();
        if (queue->getAvpacket(avPacket) != 0) {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            continue;
        }
        if (codecType == CODEC_MEDIACODEC) {

            LOGE("支持硬解码");
            if (av_bsf_send_packet(avbsfContext, avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }

            while (av_bsf_receive_packet(avbsfContext, avPacket) == 0) {
                double diff = getFrameDiffTIme(NULL, avPacket);
                LOGE("diff is %f", diff);

                 av_usleep(getDelayTime(diff) * 1000000);
                LOGE("开始解码");
                callJava->onCallDecodeAvPacket(
                        avPacket->size, avPacket->data
                );
                av_packet_free(&avPacket);
                av_free(avPacket);
                continue;
            }
            avPacket = NULL;

        } else if (codecType == CODEC_YUV) {

            LOGE("支持软解码");

            pthread_mutex_lock(&codecMutex);
            if (avcodec_send_packet(avCodecContext, avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                pthread_mutex_unlock(&codecMutex);
                continue;
            }
            AVFrame *avFrame = av_frame_alloc();
            if (avcodec_receive_frame(avCodecContext, avFrame) != 0) {
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                pthread_mutex_unlock(&codecMutex);
                continue;
            }
            if (avFrame->format == AV_PIX_FMT_YUV420P) {
                LOGE("当前视频是YUV420P格式");

                double diff = getFrameDiffTIme(avFrame, NULL);
                LOGE("diff is %f", diff);

                av_usleep(getDelayTime(diff) * 1000000);
                callJava->onCallRender(CHILD_THREAD,
                                       avCodecContext->width,
                                       avCodecContext->height,
                                       avFrame->data[0],
                                       avFrame->data[1],
                                       avFrame->data[2]);
            } else {
                LOGE("当前不是420p")

                AVFrame *YUV420Frame = av_frame_alloc();
                int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, avFrame->width,
                                                           avFrame->height, 1);
                uint8_t *buffer = static_cast<uint8_t *>(av_malloc(buffer_size * sizeof(uint8_t)));
                av_image_fill_arrays(YUV420Frame->data, avFrame->linesize, buffer,
                                     AV_PIX_FMT_YUV420P, codecpar->width, codecpar->height, 1);
                SwsContext *pSwsContext = sws_getContext(avCodecContext->width,
                                                         avCodecContext->height,
                                                         avCodecContext->pix_fmt,
                                                         avCodecContext->width,
                                                         avCodecContext->height,
                                                         AV_PIX_FMT_YUV420P,
                                                         SWS_BICUBIC, NULL, NULL, NULL);
                if (!pSwsContext) {
                    av_frame_free(&YUV420Frame);
                    av_free(YUV420Frame);
                    av_free(buffer);
                    pthread_mutex_unlock(&codecMutex);
                    continue;
                }

                sws_scale(pSwsContext,
                          reinterpret_cast<const uint8_t *const *>(avFrame->data),
                          avFrame->linesize, 0, avFrame->height,
                          YUV420Frame->data, YUV420Frame->linesize);
                float diffTIme = getFrameDiffTIme(avFrame, NULL);
                av_usleep(getDelayTime(diffTIme) * 1000000);
                callJava->onCallRender(CHILD_THREAD,
                                       avCodecContext->width,
                                       avCodecContext->height,
                                       YUV420Frame->data[0],
                                       YUV420Frame->data[1],
                                       YUV420Frame->data[2]);
                av_frame_free(&YUV420Frame);
                av_free(YUV420Frame);
                av_free(buffer);
                sws_freeContext(pSwsContext);
            }
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            LOGE(" 获得音视频包 ");
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            pthread_mutex_unlock(&codecMutex);
        }
    }
}

void WlVideo::play() {
    if (playstatus!=NULL&&!playstatus->exit){
        pthread_create(&play_thread, NULL, decode, this);

    }
}

double WlVideo::getFrameDiffTIme(AVFrame *avFrame, AVPacket *avPacket) {
    double pts;
    if (avFrame != NULL) {
        pts = av_frame_get_best_effort_timestamp(avFrame);
    }
    if (avPacket != NULL) {
        pts = avPacket->pts;
        if (pts < 0) {
            pts = avPacket->dts;
        }
    }

    if (pts == AV_NOPTS_VALUE) {
        pts = 0;
    }
    pts *= av_q2d(time_base);//获得播放时间

    if (pts > 0) {
        clock = pts;//如果pts大于0更新pts 小于0就使用上一个时间戳 
    }
    LOGE("pts %f", pts);
    LOGE("audio->clock %f", audio->clock);
    double diff = audio->clock - clock;

    return diff;
}

double WlVideo::getDelayTime(double diff) {

    if (diff > 0.003) {
        delay_time = delay_time * 2 / 3;
        if (delay_time < delay_time_base / 2) {
            delay_time = delay_time_base * 2 / 3;
        } else if (delay_time > delay_time_base * 2) {
            delay_time = delay_time_base * 2;
        }
    } else if (diff < -0.003) {
        delay_time = delay_time * 3 / 2;
        if (delay_time < delay_time_base / 2) {
            delay_time = delay_time_base * 2 / 3;
        } else if (delay_time > delay_time_base * 2) {
            delay_time = delay_time_base * 2;
        }
    }
    if (diff > 0.05) {
        delay_time = 0;
    } else if (diff < -0.05) {
        delay_time = delay_time_base * 2;
    }
    if (fabs(diff) > 10) {
        delay_time = delay_time_base;
    }
    LOGE("getDelayTime %f", delay_time);
    return delay_time;
}

void WlVideo::release() {
    if (queue!=NULL){
        queue->notice();
    }
    pthread_join(play_thread,NULL);
    if (queue != NULL) {
        // queue->clearAvpacket();
        delete (queue);
        queue = NULL;
    }
    if (avbsfContext != NULL) {
        av_bsf_free(&avbsfContext);
        avbsfContext = NULL;
    }
    if (avCodecContext != NULL) {
        pthread_mutex_lock(&codecMutex);
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
        pthread_mutex_unlock(&codecMutex);
    }

    if (playstatus != NULL) {
        playstatus = NULL;
    }
    if (callJava != NULL) {
        callJava = NULL;
    }


}

WlVideo::~WlVideo() {

}
